/**
 * Copyright (c) 2021 Wei-Lun Hsu. All Rights Reserved.
 */
/** @file zb32l03x_hal_trng.c
 *
 * @author Wei-Lun Hsu
 * @version 0.1
 * @date 2021/09/27
 * @license
 * @description
 *      This file provides firmware functions to manage the following
 *      functionalities of the True Random Number Generator (TRNG) peripheral:
 *          + Initialization and configuration functions
 *          + Peripheral Control functions
 *          + Peripheral State functions
  @verbatim
  ==============================================================================
                     ##### How to use this driver #####
  ==============================================================================
  [..]
      The TRNG HAL driver can be used as follows:

      (#) Enable the TRNG controller clock using __HAL_RCC_TRNG_CLK_ENABLE() macro
          in HAL_TRNG_MspInit().
      (#) Activate the TRNG peripheral using HAL_TRNG_Init() function.
      (#) Wait until the 32 bit Random Number Generator contains a valid
          random data using (polling/interrupt) mode.
      (#) Get the 32 bit random number using HAL_TRNG_GenerateRandomNumber() function.

    ##### Callback registration #####
    ==================================

    [..]
    The compilation define USE_HAL_TRNG_REGISTER_CALLBACKS when set to 1
    allows the user to configure dynamically the driver callbacks.

    [..]
    Use Function HAL_TRNG_RegisterCallback() to register a user callback.
    Function HAL_TRNG_RegisterCallback() allows to register following callbacks:
    (+) ErrorCallback             : TRNG Error Callback.
    (+) MspInitCallback           : TRNG MspInit.
    (+) MspDeInitCallback         : TRNG MspDeInit.
    This function takes as parameters the HAL peripheral handle, the Callback ID
    and a pointer to the user callback function.

    [..]
    Use function HAL_TRNG_UnRegisterCallback() to reset a callback to the default
    weak (surcharged) function.
    HAL_TRNG_UnRegisterCallback() takes as parameters the HAL peripheral handle,
    and the Callback ID.
    This function allows to reset following callbacks:
    (+) ErrorCallback             : TRNG Error Callback.
    (+) MspInitCallback           : TRNG MspInit.
    (+) MspDeInitCallback         : TRNG MspDeInit.

    [..]
    For specific callback ReadyDataCallback, use dedicated register callbacks:
    respectively HAL_TRNG_RegisterReadyDataCallback() , HAL_TRNG_UnRegisterReadyDataCallback().

    [..]
    By default, after the HAL_TRNG_Init() and when the state is HAL_TRNG_STATE_RESET
    all callbacks are set to the corresponding weak (surcharged) functions:
    example HAL_TRNG_ErrorCallback().
    Exception done for MspInit and MspDeInit functions that are respectively
    reset to the legacy weak (surcharged) functions in the HAL_TRNG_Init()
    and HAL_TRNG_DeInit() only when these callbacks are null (not registered beforehand).
    If not, MspInit or MspDeInit are not null, the HAL_TRNG_Init() and HAL_TRNG_DeInit()
    keep and use the user MspInit/MspDeInit callbacks (registered beforehand).

    [..]
    Callbacks can be registered/unregistered in HAL_TRNG_STATE_READY state only.
    Exception done MspInit/MspDeInit that can be registered/unregistered
    in HAL_TRNG_STATE_READY or HAL_TRNG_STATE_RESET state, thus registered (user)
    MspInit/DeInit callbacks can be used during the Init/DeInit.
    In that case first register the MspInit/MspDeInit user callbacks
    using HAL_TRNG_RegisterCallback() before calling HAL_TRNG_DeInit()
    or HAL_TRNG_Init() function.

    [..]
    When The compilation define USE_HAL_TRNG_REGISTER_CALLBACKS is set to 0 or
    not defined, the callback registration feature is not available
    and weak (surcharged) callbacks are used.

  @endverbatim
 */


#include "zb32l03x_hal.h"

/** @addtogroup ZB32L03x_HAL_Driver
  * @{
  */


/** @defgroup TRNG TRNG
  * @brief TRNG HAL module driver.
  * @{
  */
#if defined(HAL_TRNG_MODULE_ENABLED)

//=============================================================================
//                  Constant Definition
//=============================================================================

//=============================================================================
//                  Macro Definition
//=============================================================================

//=============================================================================
//                  Structure Definition
//=============================================================================

//=============================================================================
//                  Global Data Definition
//=============================================================================

//=============================================================================
//                  Private Function Definition
//=============================================================================
/**
  * @brief  Initializes the TRNG MSP.
  * @param  hcrc: pointer to a CRC_HandleTypeDef structure that contains
  *         the configuration information for CRC
  * @retval None
  */
__weak void HAL_TRNG_MspInit(HAL_TRNG_Handle_TypeDef *hTrng)
{
    /* Prevent unused argument(s) compilation warning */
    UNUSED(hTrng);
}

/**
  * @brief  DeInitializes the TRNG MSP.
  * @param  hcrc: pointer to a CRC_HandleTypeDef structure that contains
  *         the configuration information for CRC
  * @retval None
  */
__weak void HAL_TRNG_MspDeInit(HAL_TRNG_Handle_TypeDef *hTrng)
{
    /* Prevent unused argument(s) compilation warning */
    UNUSED(hTrng);
}

/**
  * @brief  Data Ready callback in non-blocking mode.
  * @param  hTrng pointer to a HAL_TRNG_Handle_TypeDef structure that contains
  *                the configuration information for TRNG.
  * @param  random32bit generated random number.
  * @retval None
  */
__weak void HAL_TRNG_ReadyDataCallback(HAL_TRNG_Handle_TypeDef *hTrng)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hTrng);
}

/**
  * @brief  TRNG error callbacks.
  * @param  hTrng pointer to a HAL_TRNG_Handle_TypeDef structure that contains
  *                the configuration information for TRNG.
  * @retval None
  */
__weak void HAL_TRNG_ErrorCallback(HAL_TRNG_Handle_TypeDef *hTrng)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hTrng);
}

//=============================================================================
//                  Public Function Definition
//=============================================================================
HAL_StatusTypeDef HAL_TRNG_Init(HAL_TRNG_Handle_TypeDef *hTrng)
{
    /* Check the TRNG handle allocation */
    if( hTrng == NULL )
    {
        return HAL_ERROR;
    }

    /* Check the parameters */
    assert_param(IS_TRNG_ALL_INSTANCE(hTrng->Instance));

    if (hTrng->State == HAL_TRNG_STATE_RESET)
    {
        /* Allocate lock resource and initialize it */
        hTrng->Lock = HAL_UNLOCKED;

        /* Init the low level hardware */
        HAL_TRNG_MspInit(hTrng);
    }

    /* Change RNG peripheral state */
    hTrng->State = HAL_TRNG_STATE_BUSY;

    hTrng->Instance->CR |= hTrng->Init.ClockDiv;
    if(hTrng->Init.IS_EnableCED == true) hTrng->Instance->CR |= TRNG_CR_CED;

    /* Enable the RNG Peripheral */
    __HAL_TRNG_ENABLE(hTrng);

    /* Initialize the RNG state */
    hTrng->State = HAL_TRNG_STATE_READY;

    /* Return function status */
    return HAL_OK;
}


HAL_StatusTypeDef HAL_TRNG_DeInit(HAL_TRNG_Handle_TypeDef *hTrng)
{
    /* Check the CRC handle allocation */
    if( hTrng == NULL )
    {
        return HAL_ERROR;
    }
    /* Disable the TRNG Peripheral */
    CLEAR_BIT(hTrng->Instance->CR, TRNG_CR_TRNGIE | TRNG_CR_TRNGEN | TRNG_CR_CED);

    /* Clear TRNG interrupt status flags */
    CLEAR_BIT(hTrng->Instance->SR, TRNG_SR_CEIS | TRNG_SR_SEIS);

    /* DeInit the low level hardware */
    HAL_TRNG_MspDeInit(hTrng);

    /* Update the TRNG state */
    hTrng->State = HAL_TRNG_STATE_RESET;

    /* Release Lock */
    __HAL_UNLOCK(hTrng);

    /* Return function status */
    return HAL_OK;
}

HAL_StatusTypeDef HAL_TRNG_GenerateRandomNumber(HAL_TRNG_Handle_TypeDef *hTrng, uint32_t *random32bit)
{
    uint32_t tickstart;
    HAL_StatusTypeDef status = HAL_OK;

    /* Process Locked */
    __HAL_LOCK(hTrng);

    /* Check RNG peripheral state */
    if (hTrng->State == HAL_TRNG_STATE_READY)
    {
        /* Change RNG peripheral state */
        hTrng->State = HAL_TRNG_STATE_BUSY;

        /* Get tick */
        tickstart = HAL_GetTick();

        /* Check if data register contains valid random data */
        while (__HAL_TRNG_GET_FLAG(hTrng, TRNG_FLAG_DRDY) == RESET)
        {
            if ((HAL_GetTick() - tickstart) > TRNG_TIMEOUT_VALUE)
            {
                /* New check to avoid false timeout detection in case of preemption */
                if (__HAL_TRNG_GET_FLAG(hTrng, TRNG_FLAG_DRDY) == RESET)
                {
                    hTrng->State = HAL_TRNG_STATE_READY;
                    /* Process Unlocked */
                    __HAL_UNLOCK(hTrng);
                    return HAL_ERROR;
                }
            }
        }
        /* Get a 32bit Random number */
        *random32bit = hTrng->Instance->DR;

        hTrng->State = HAL_TRNG_STATE_READY;
    }
    else
    {
        status = HAL_ERROR;
    }

    /* Process Unlocked */
    __HAL_UNLOCK(hTrng);

    return status;
}

HAL_StatusTypeDef HAL_TRNG_GenerateRandomNumber_IT(HAL_TRNG_Handle_TypeDef *hTrng)
{
    HAL_StatusTypeDef status = HAL_OK;

    /* Process Locked */
    __HAL_LOCK(hTrng);

    /* Check RNG peripheral state */
    if (hTrng->State == HAL_TRNG_STATE_READY)
    {
        /* Change RNG peripheral state */
        hTrng->State = HAL_TRNG_STATE_BUSY;

        /* Enable the RNG Interrupts: Data Ready, Clock error, Seed error */
        __HAL_TRNG_ENABLE_IT(hTrng);
    }
    else
    {
        /* Process Unlocked */
        __HAL_UNLOCK(hTrng);

        status = HAL_ERROR;
    }

  return status;
}

uint32_t HAL_TRNG_GetRandomNumber(HAL_TRNG_Handle_TypeDef *hTrng)
{
    uint32_t RandomNumber = 0U;

    if (HAL_TRNG_GenerateRandomNumber(hTrng, &RandomNumber) == HAL_OK)
    {
        return RandomNumber;
    }
    else
    {
        return 0U;
    }
}

uint32_t HAL_TRNG_GetRandomNumber_IT(HAL_TRNG_Handle_TypeDef *hTrng)
{
    uint32_t random32bit = 0U;

    /* Process locked */
    __HAL_LOCK(hTrng);

    /* Change TRNG peripheral state */
    hTrng->State = HAL_TRNG_STATE_BUSY;

    /* Get a 32bit Random number */
    random32bit = hTrng->Instance->DR;

    /* Enable the TRNG Interrupts: Data Ready, Clock error, Seed error */
    __HAL_TRNG_ENABLE_IT(hTrng);

    /* Return the 32 bit random number */
    return random32bit;
}

uint32_t HAL_TRNG_ReadLastRandomNumber(HAL_TRNG_Handle_TypeDef *hTrng)
{
  return (hTrng->Instance->DR);
}

void HAL_TRNG_IRQHandler(HAL_TRNG_Handle_TypeDef *hTrng)
{
    uint32_t rngclockerror = 0U;

    /* RNG clock error interrupt occurred */
    if (__HAL_TRNG_GET_IT(hTrng, TRNG_IT_CEI) != RESET)
    {
        /* Update the error code */
        rngclockerror = 1U;
    }
    else if (__HAL_TRNG_GET_IT(hTrng, TRNG_IT_SEI) != RESET)
    {
        rngclockerror = 1U;
    }
    else
    {
        /* Nothing to do */
    }

    if (rngclockerror == 1U)
    {
        /* Change RNG peripheral state */
        hTrng->State = HAL_TRNG_STATE_ERROR;

        /* Call legacy weak Error callback */
        HAL_TRNG_ErrorCallback(hTrng);

        /* Clear the clock error flag */
        __HAL_TRNG_CLEAR_IT(hTrng, TRNG_IT_CEI | TRNG_IT_SEI);

        return;
    }

    /* Check RNG data ready interrupt occurred */
    if (__HAL_TRNG_GET_IT(hTrng, TRNG_IT_DRDY) != RESET)
    {
        /* Generate random number once, so disable the IT */
        __HAL_TRNG_DISABLE_IT(hTrng);

        /* Get the 32bit Random number (DRDY flag automatically cleared) */

        if (hTrng->State != HAL_TRNG_STATE_ERROR)
        {
            /* Change RNG peripheral state */
            hTrng->State = HAL_TRNG_STATE_READY;
            /* Process Unlocked */
            __HAL_UNLOCK(hTrng);

            /* Call legacy weak Data Ready callback */
            HAL_TRNG_ReadyDataCallback(hTrng);
        }
    }
}
#endif

/**
  * @}
  */ /* group TRNG TRNG */

/**
  * @}
  */ /* group ZB32L03x_HAL_Driver */
